﻿using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using MDK;
using MDK.Physics;
using ZedGraph;
using Tao.FreeGlut;
using Tao.OpenGl;
using Tao.Platform.Windows;

namespace Scattering3d
{
    public class Scattering3d : GraphicPrimitive
    {
        /// <summary>
        /// Объект альфа-частицы
        /// </summary>
        MovingParticle Alpha;

        /// <summary>
        /// Объект ядра
        /// </summary>
        MovingParticle Nucl;

        /// <summary>
        /// Все наши частицы
        /// </summary>
        List<MovingParticle> Particles = new List<MovingParticle>();

        /// <summary>
        /// Нижняя граница b
        /// </summary>
        public float bLow;

        /// <summary>
        /// Верхняя граница b
        /// </summary>
        public float bHigh;

        /// <summary>
        /// Массив прицельных расстояний
        /// </summary>
        public float[] bArray;

        // мы должны их хранить, для передачи обратно, при редактировании параметров
        private double pNum;
        private double bMin;
        private double bMax;

        #region Конструкторы
        public Scattering3d()
            : base(0, 0, new Size())
        {
            AddParametersToTemplate();
            this.Needs = ExperimentNeeds.OpenGL;
        }

        public Scattering3d(int Left, int Top, Size Size)
            : base(Left, Top, Size)
        {
            // передаём размеры
            this.Left = Left;
            this.Top = Top;

            this.Size = Size;

            //создаём список по умолчанию
            AddParametersToTemplate();
            this.Needs = ExperimentNeeds.OpenGL;
        }
        #endregion

        #region Методы взаимодействия с главным модулем
        public override ExperimentAbout GetAbout()
        {
            return new ExperimentAbout("Scattering3d", "Трёхмерное рассеивание",
                "Модель кулоновского рассеяния одних частиц на других, при условии, что частицы заряженны положительно.\r\n Количество рассеивающихся астиц задаётся параметром pN\r\n" +
                "Начальная скорость рассеивающихся частиц задаётся параметром V. \r\n" +
                "Заряд рассеятеля равен Q. Прицельное расстояние задаётся случайным способом от bMin, до bMax.\r\nЗакон кулона: F=k*q1*q2/r^2");
        }

        public override void GL_camera(double[] cameraPos, double[] targetPos, double[] rotateA)
        {
            Glu.gluLookAt(cameraPos[0], cameraPos[1], cameraPos[2], targetPos[0], targetPos[1], targetPos[2], 
                0, 1, 0); // еденичный вектор смотрит вверх
            //Gl.glTranslatef((float)targetPos[0], (float)targetPos[1], (float)targetPos[2]);
            Gl.glScaled(targetPos[3], targetPos[3], targetPos[3]);

            Gl.glRotated(-rotateA[0], 1, 0, 0);
            Gl.glRotated(rotateA[1], 0, 1, 0);
            Gl.glRotated(-rotateA[2], 0, 0, 1);
            
        }

        public override void  GL_init(int w, int h)
        {
         
            Gl.glClearColor(0, 0, 0, 1);
           

            Gl.glMatrixMode(Tao.OpenGl.Gl.GL_PROJECTION);
            Gl.glLoadIdentity();
            Gl.glViewport(0, 0, w, h);
            //Glu.gluPerspective(45, w / h, 0.1, 500);
            Gl.glOrtho(-w / 2, w / 2, -h / 2, h / 2, -1000, 1000);

            Gl.glMatrixMode(Tao.OpenGl.Gl.GL_MODELVIEW);
            Gl.glLoadIdentity();

            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);        // Clear Screen And Depth Buffer
            Gl.glLoadIdentity();

            Gl.glEnable(Gl.GL_DEPTH_TEST);
        }

        private void AddParametersToTemplate()
        {
            ParameterListTemplate = new ParameterList();

            ParameterListTemplate.Add("Заряд рассеявателя", "Q", 47);
            ParameterListTemplate.Add("Масса рассеявателя", "M", 327);
            ParameterListTemplate.Add("Заряд рассеяваемой частицы", "q", 400);
            ParameterListTemplate.Add("Масса рассеяваемой частицы", "m", 6.7);
            ParameterListTemplate.Add("Минимальное прицельное расстояние", "bMin", 1);
            ParameterListTemplate.Add("Максимальное прицельное расстояние", "bMax", 50);
            ParameterListTemplate.Add("Количество частиц", "pN", 15);
            ParameterListTemplate.Add("Начальная скороть частиц", "V", 1);
        }
        List<int> angle_l = new List<int>();
        public override void SetParameters(ParameterList pList)
        {
            Nucl = new MovingParticle();
            Nucl.Q = pList["Q"];
            Nucl.m = pList["M"];
            
            Alpha = new MovingParticle();
            Alpha.vx = pList["V"]; // начальная скорость
            Alpha.Q = pList["q"];
            Alpha.m = pList["m"];
            AlphaParticle.Nucl = Nucl; // для всех альфа-частиц
            SetParticles(Alpha, Nucl);

            if (pList["bMin"] > pList["bMax"])
            {
                MessageBox.Show("bMin должно быть меньше bHigh", "Ошибка!");
                return;
            }


            // сохраняем, для будующей передачи обратно через GetParameters()
            this.pNum = pList["pN"];
            this.bMin = pList["bMin"];
            this.bMax = pList["bMax"];

            Random rnd_angle = new Random();
            for (int a = 0; a < pNum; a++)
            {
                angle_l.Add(rnd_angle.Next(1, 360));
            }
            b = GenerateB((int)pNum, (int)bMin, (int)bMax);
        }

        void SetParticles(MovingParticle Alpha, MovingParticle Nucl)
        {
            this.Alpha = Alpha;
            this.Nucl = Nucl;
        }


        public override ParameterList GetParameters()
        {
            ParameterList RetList = ParameterListTemplate;
            //RetList["Q"] = Nucl.Q;
            //RetList["V"] = Alpha.vx;
            RetList["pN"] = this.pNum;
            RetList["bMin"] = this.bMin;
            RetList["bMax"] = this.bMax;


            return RetList;
        }

        public override PointPairList GetResults()
        {
            makeTeta();
            return tetaList;
        }

        #endregion

        #region Визуализация
        /// <summary>
        /// Метод рисования
        /// </summary>
        /// <param name="e"></param>
        public override void Draw(System.Windows.Forms.PaintEventArgs e)
        {
            // чистим мусор
            Particles.Clear();

            Glu.GLUquadric quadric = Glu.gluNewQuadric();

            //инициализируемся
            Nucl.x = 0;
            Nucl.y = 0;

            AddParticles(b);
            

            //--------------
            // начинаем рисовать
            //--------------

            
            //Gl.glHint(Gl.GL_LINE_SMOOTH_HINT, Gl.GL_FASTEST);
            Gl.glEnable(Gl.GL_LINE_SMOOTH);

            Gl.glLineWidth(1);
            Gl.glColor4d(0.8, 0.4, 0, 1);
            drawP();

            Gl.glDisable(Gl.GL_LINE_SMOOTH);
            // ядро
            Gl.glPushMatrix();
            Gl.glTranslatef(Nucl.x, Nucl.y, 0);
            Gl.glColor4d(0, 0, 0.5, 1);
            Glu.gluQuadricDrawStyle(quadric, Glu.GLU_LINE);
            Glu.gluSphere(quadric, 10, 24, 24);
            //Gl.glTranslatef(-Nucl.x, -Nucl.y, 0);
            Gl.glPopMatrix();

            
            

            Gl.glFlush();
            Glu.gluDeleteQuadric(quadric);
        }
        void drawP()
        {
            int k = 0;
            foreach (MovingParticle a in Particles)
            {
                Gl.glPushMatrix();
                Gl.glRotatef(angle_l[k], 1, 0, 0);
                //Gl.glLineWidth(2.0f);
                Gl.glBegin(Gl.GL_LINE_STRIP);
                for (int j = 0; j < this.Width * 3 / 4; j++)
                {
                    Gl.glVertex3d(-a.x, a.y, 0);
                    a.Move(Nucl);
                    //Nucl.Move(a);
                }
                Gl.glEnd();
                Gl.glPopMatrix();
                k++;
               //MessageBox.Show(String.Format("paritcle a:\n mass: {0}\n vx: {1}", a.m, a.vx));
            }
            //MessageBox.Show(String.Format("Nucl.x: {0}, Nucl.y: {1}", Nucl.x, Nucl.y));
        }
        
        #endregion

        #region Моделирование
        public void AddParticles(float[] bArray)
        {
            Random rnd = new Random();

            for (int i = 0; i < bArray.Length; i++)
            {
                Particle m = new Particle();
                m.x = Nucl.x - this.Size.Width / 2;
                m.y = Nucl.y - bArray[i];
                m.Q = Alpha.Q;
                m.Mass = Alpha.m;
                m.vx = Alpha.vx;
                
                MovingParticle p = new MovingParticle(m, bArray[i]);
                p.x0 = 0;
                p.y0 = bArray[i];

                Particles.Add(p);
            }
        }

        float[] teta_array;

        public float Actg(double value)
        {
            return ((float)Math.PI / 2 - (float)Math.Atan(value));
        }
        PointPairList tetaList = new PointPairList();
        private float CountTeta(float b)
        {

            //return 20 * Actg((float)((Alpha.m * b * (Alpha.V * Alpha.V) / (Z * 2 * 2.56 * (float)Math.Pow(10, -3))));
            // z - заряд ядра
            //const double p = 1; //порядок, на что домножать
            //double r = Alpha.m * Alpha.V * Alpha.V * b;


            //return (2 * Actg(p * r));
            //return (2 * Actg((Alpha.m * b * Alpha.vx * Alpha.vx) / (2 * Nucl.Q)));
            return (2 * Actg((b * Alpha.vx * Alpha.vx) / (2 * Nucl.Q)));
        }
        float[] b;
        private void makeTeta()
        {

            tetaList.Clear();
            teta_array = new float[6];
            float curr_teta;
            foreach (MovingParticle p in Particles)
            {

                curr_teta = (float)Math.Atan((p.y-p.y0) / (p.x-p.x0));//CountTeta(b_elem);
                //MessageBox.Show("Teta: " + curr_teta.ToString() + ", b: " + b_elem);
                if (curr_teta >= 0 && curr_teta <= Math.PI / 3)
                    teta_array[0]++;
                else if (curr_teta > Math.PI / 3 && curr_teta <= 2 * Math.PI / 3)
                    teta_array[1]++;
                else if (curr_teta > 2 * Math.PI / 3 && curr_teta <= Math.PI)
                    teta_array[2]++;
                else if (curr_teta > Math.PI && curr_teta <= 4 * Math.PI / 3)
                    teta_array[3]++;
                else if (curr_teta > 4 * Math.PI / 3 && curr_teta <= 5 * Math.PI / 3)
                    teta_array[4]++;
                else if (curr_teta > 5 * Math.PI / 3 && curr_teta <= 2 * Math.PI)
                    teta_array[5]++;
            }

            for (int i = 0; i < 6; i++)
            {
                tetaList.Add((i * Math.PI / 3) * (180 / Math.PI), teta_array[i]);
            }

        }

        private float[] GenerateB(int pNum, int bLow, int bHigh)
        {
            Random rnd = new Random();
            float[] bArray = new float[pNum];

            for (int i = 0; i < bArray.Length; i++)
            {
                bArray[i] = rnd.Next(bLow, bHigh);
            }

            b = bArray;
            return bArray;
        }

   
        #endregion

    }
}
